# -*- coding: utf-8 -*-
"""
Comment:
	
	
	
"""

import os
import numpy as np
import matlab as ml
import matplotlib as mpl
import matplotlib.pyplot as plt
import itertools as it
import copy
from cdlp55 import mnl,getRandomstreams,cdlp,getRandomOSwithCDLP,efficientSets_largest_marginal_revenue_algorithm,calc_upsellpossibilities,getRandomstreams_Buyer_j
from scipy.stats import uniform
import matlab.engine
import scipy.io
from math import e
import time
from scipy.stats import binom
from mpl_toolkits.mplot3d import Axes3D
from InSt2 import seen_odd_nonodd_products
from InSt6 import offerset_based
from InSt2b import seen_odd_nonodd_products_V2
runs = np.arange(nRuns)

#Probabilities
probMNL_Segments = mnl(offerSets,prices,segQ,segments,segScale,segNP)                               #function based on multinomial logit
probMNL = np.dot(probMNL_Segments,segLambdas)
RS = np.sum(probMNL[:,:nProducts]*prices,1)  
Qt = np.sum(probMNL[:,:nProducts],1)  
effSets = efficientSets_largest_marginal_revenue_algorithm(Qt,RS,os2)    

#CDLP control and randomization of offer sets
nn = os.getcwd()
print(nn)
[Ts,ofv] = cdlp(offerSets,RS,capcon,cap,probMNL,nTimeperiods,products,eng)                           #calculate timeslots from CDLP
OS_run_t = getRandomOSwithCDLP(nTimeperiods,nRuns,Ts,seed=1234)                                         #random OS with inversion method                    

#Customer Streams (randomSegments,randomQualityindices,randomNopurch) for BH and UH III
[rSeg,rQ,rNP] = getRandomstreams(products,segments,segLambdas,segQ,segScale,timeperiods,runs,1,segNP,seed=12345)    #random customer streams with Monte-Carlo similation
filename = 'Randomstreams3_'+ str(inst) + '_' + str(nTimeperiods) + 'xWSK.npz'
path = dictt + '/' + filename
if filename in os.listdir(dictt):
    a = np.load(path)
    rSeg2 = a['arr_0']
    rQ2 = a['arr_1']
    rNP2 = a['arr_2']
else:
    [rSeg2,rQ2,rNP2] = getRandomstreams(products,segments,segLambdas,segQ,segScale,timeperiods,runs,nEvalWSK,segNP,seed=nTimeperiods*(nProducts+1)*nRuns+1)
    np.savez(path,rSeg2,rQ2,rNP2)

if np.where(Ts!=0)[0].size>4:
    print('\n\nToo many offered offer sets for Matlab in this instance! Change Matlab code of of#c_p or instance parameters...')
else:
    #Arrays to save data
    overview = np.zeros((nRuns,nTimeperiods,4)) 
    tickets_sold = np.zeros((nSegments,nProducts,nRuns),dtype=np.int32)
    revenues_BH_run = np.zeros(nRuns)       #booking horizon
    revenues_UP_run = np.zeros(nRuns)       #upsell problem mit stinf
    revenues_RCS_run_eval = np.zeros((nRuns,nEvalWSK))
    revenues_UH_run_eval = np.zeros((nRuns,nEvalWSK))
    revenues_UH2_run_eval = np.zeros((nRuns,nEvalWSK))
    revenues_UH_run = np.zeros(nRuns)       #upsell horizon b-f first,then e-b
    revenues_RCS_run = np.zeros(nRuns) 
    revenues_RCS2_run = np.zeros(nRuns)
    revenues_optn = np.zeros(nRuns)
    revenues_UHbinom_run = np.zeros(nRuns)  #binomial upsell horizon
    revenues_WSK_run_eval = np.zeros((nRuns,nEvalWSK))
    revenues_UH_WSK_run = np.zeros(nRuns)   #upsell horizon mit Evaluationen
    revenues_UH_newEps_run_eval = np.zeros((nRuns,nEvalWSK))
    revenues_UH_newEps_run = np.zeros(nRuns) 
    up_overview_I = np.zeros((1,6))
    up_overview_II = np.zeros((1,6))
    up_overview_III = np.zeros((1,6))
    up_overview_IV = np.zeros((1,6))
    up_overview_V = np.zeros((1,6))
    cap_overview = np.zeros((nRuns,6,cap.shape[0]))
    deniedUp = np.zeros((nRuns,nProducts,5))
    checkcapconst = np.zeros((nRuns,1))
    np.random.seed(1992)
    randi = uniform.rvs(loc=0,scale=1,size=[nRuns,nTimeperiods,nEvalWSK])
    groups = np.zeros(nRuns) 
    ppp_and_group = np.zeros((nRuns,nTimeperiods,7))
    
    time_elapsed_cont = np.zeros(nRuns)
    time_elapsed_BI = np.zeros(nRuns)
    
    Name = str(inst) + '_stinf' + str(stinf) + '_' + bound + str(eps) + '_' + uphierar + \
    '_nyp' + str(nyp) + '_' + str(nEvalWSK) + 'xWSK' + mode # + '_newEpsEval'
    
    #Simulation 
    for runn in runs: #np.array([0,3890,9372]):
        np.random.seed(999)
        #Booking Horizon
        reservationsInRun = np.zeros((nSegments,nProducts),dtype=np.int32)
        resCap = np.copy(cap).reshape(-1)
        for t in timeperiods[0:int(Tshare*nTimeperiods)]:
            os2 = offerSets[OS_run_t[t,runn].astype(int),:]
            seg = rSeg[t,runn]
            q = rQ[t,:,runn].reshape(-1)
            nopurch = rNP[t,runn]
            utilities = q - np.squeeze(prices[:])
            utilities[capcon @ resCap == 0] = -np.inf
            utilities[os2==0] = -np.inf
            if (seg >= 0) and np.any(utilities>nopurch):
                y = np.argmax(utilities)
                reservationsInRun[seg,y] += 1
                resCap -= capcon[y,:]
                overview[runn,t,:] = np.array([t,seg,y,OS_run_t[t,runn]])
            else:
                overview[runn,t,:] = np.array([t,seg,-10,OS_run_t[t,runn]])
        overview[runn,int(Tshare*nTimeperiods):nTimeperiods,:] = np.array([-100,-1000,-10,0])
                
        #Evaluate solution
        tickets_sold[:,:,runn] = reservationsInRun        
        revenues_BH_run[runn] = np.sum(np.squeeze(prices)*reservationsInRun)
        rem_cap = cap.transpose() - np.sum(reservationsInRun @ capcon,axis=0)
        cap_overview[runn,0,:] = rem_cap
        if runn%1000 == 0:
            print(runn,rem_cap)
#        overviewx = copy.deepcopy(overview)
        overviewx = np.copy(overview)
        
        if np.any(rem_cap[0]!=cap):
            if np.any(rem_cap[0][1:]>0):
                if stinf == 2: #standard method based on offer sets
                   prob,nns,values,uniqSeg,revenues_UP_run,revenues_UHbinom_run,time_elapsed_cont,checkcapconst = \
                   seen_odd_nonodd_products(sharepairs,szpairs,timeperiods,segNP,mode,inst,revenues_UHbinom_run,bound,eps,checkcapconst,overview,capcon,runn,nRes, \
                                    rem_cap,nProducts,offerSets,prices,eng,segQ,segScale,cap,revenues_UP_run,time_elapsed_cont,uphierar,nyp,segLambdas)
                elif stinf == 6: #standard method based on offer sets
                    prob,nns,values,uniqSeg,revenues_UP_run,revenues_UHbinom_run,time_elapsed_cont,checkcapconst,timertime = \
                    offerset_based(sharepairs,szpairs,segNP,mode,inst,revenues_UHbinom_run,bound,eps,checkcapconst,overview,capcon,runn,nRes,rem_cap,\
                                   nProducts,offerSets,prices,eng,segQ,segScale,cap,revenues_UP_run,time_elapsed_cont,uphierar,nyp,segLambdas)
                elif stinf == 9: #standard method based on offer sets
                    prob,nns,values,uniqSeg,revenues_UP_run,revenues_UHbinom_run,time_elapsed_cont,checkcapconst = \
                    seen_odd_nonodd_products_V2(sharepairs,szpairs,timeperiods,segNP,mode,inst,revenues_UHbinom_run,bound,eps,checkcapconst,overview,capcon,runn,nRes,rem_cap,\
                                   nProducts,offerSets,prices,eng,segQ,segScale,cap,revenues_UP_run,time_elapsed_cont,uphierar,nyp,segLambdas)
                    
                
                #UPSELL HORIZON NUMBER I - first business-first, then eco-business
                hi = []
                for l in np.arange(values.shape[0]):
                    ll = int(values[l,0])
                    hi.append(tuple((values[l,0],values[l,1],(values[l,2] - prices[uniqSeg[ll,1]])[0],values[l,2],np.where(capcon[int(values[ll,1])]!=0)[0][0],nns[ll])))
                dtyp = [('Seg',int),('k',int),('revenue',float),('r_opt','float'),('compartment',int),('persons',float)]
                arr = np.array(hi,dtyp)
                hi2 = np.sort(arr,order=['compartment','revenue'])[::-1]
                values2 = np.zeros((values.shape[0],values.shape[1]+2))
                for l in np.arange(values.shape[0]):
                    for m in np.arange(values.shape[1]+2):
                        values2[l,m] = hi2[l][m]   
                        
                all_cop_rem_cap = np.zeros((nEvalWSK,nRes))
                if mode == '':
                    numb = 1
                elif mode == '_p2':
                    numb = nEvalWSK
                for ev in np.arange(numb):
                    uppies = [] 
                    no = -1
                    cop_rem_cap = np.copy(rem_cap)
                    for s in values2[:,0].astype(np.int):
                        no += 1
                        nprice = values[s,2]
                        nprices = np.copy(prices)
                        nprices[int(values2[no,1])] = nprice                            #replace price for upsell product
                        cust = np.where(np.all(overview[runn,:,1:4]==uniqSeg[s],axis=1))                 #get customers of segment
                        for c in cust[0]:
                            if mode == '':
                                q = rQ[c,:,runn,ev]
                            elif mode == '_p2':
                                q = rQ2[c,:,runn,ev]
                            utilities = q - np.squeeze(nprices)                         #calculate new utilitites with replaced price  
                            ut_prod = utilities[uniqSeg[s,1]]                           #get utility of product bought in BH 
                            y = q[int(values2[no,1])] - nprices[int(values2[no,1])]     #introduce/replace upsell product
                            yy = int(values2[no,1])
                            #Teil nach dem 'and' prüft, ob keine Kapazität mehr auf der Ressource, zu der das Upsell geht
                            if y >= ut_prod and np.isin(y,utilities[capcon @ cop_rem_cap[0] == 0],invert=True):
                                revenues_UH_run_eval[runn,ev] += nprice - prices[uniqSeg[s,1]] 
                                cop_rem_cap[0] += capcon[uniqSeg[s,1],:] - capcon[yy,:]
                                uppies.append(list((runn,c,uniqSeg[s,1],yy,s,nprice-prices[uniqSeg[s,1]][0])))
                            elif y >= ut_prod:
                                deniedUp[runn,yy,0] += 1 
                    if len(uppies)!=0:
                        up_overview_I = np.vstack((up_overview_I,np.array(uppies)))   
                    all_cop_rem_cap[ev] = cop_rem_cap[0]
                revenues_UH_run[runn] = np.mean(revenues_UH_run_eval[runn,np.arange(numb)])
                cap_overview[runn,1,:] = np.mean(all_cop_rem_cap,0)
                
                #UPSELL HORIZON BINOMIAL
                if stinf != 6 or (stinf == 6 and mode=='_p2'): 
                    offerSets2 = np.copy(offerSets)
                    lista = list()
                    for i in np.arange(uniqSeg.shape[0]):
                        vecmask = np.all(np.array([overview[runn,:,1:4]])==uniqSeg[i],2)
                        lista.append((timeperiods[vecmask[0]]))
                    cop_rem_cap = np.copy(rem_cap)
                    #Segments according to full information
                    uniqS, counts = np.unique(overviewx[runn,:,1:4],return_counts=True,axis=0)
                    uniqSegx = uniqS[uniqS[:,1]!=-10].astype(int)                                         #delete nopurch rows
                    counts2x = counts[uniqS[:,1]!=-10] 
                    counts2x = counts2x[np.isin(uniqSegx[:,1],np.where(capcon[:,nRes-1]==1),invert=True)]
                    uniqSegx = uniqSegx[np.isin(uniqSegx[:,1],np.where(capcon[:,nRes-1]==1),invert=True)]   #delete those in highest compartment
                    if  cop_rem_cap[0][1:][-1] == 0:
                        counts2x = counts2x[np.isin(uniqSegx[:,1],np.where(capcon[:,nRes-2]==1),invert=True)]
                        uniqSegx = uniqSegx[np.isin(uniqSegx[:,1],np.where(capcon[:,nRes-2]==1),invert=True)]   #delete those in highest compartment
                    #match lista to different segments
                    values3 = np.zeros([uniqSegx.shape[0],6])
                    groups[runn] = uniqSegx.shape[0]
                    for i in np.arange(uniqSegx.shape[0]):
                        vecmask = np.all(np.array([overviewx[runn,:,1:4]])==uniqSegx[i],2)
                        kunden = timeperiods[vecmask[0]]
                        ll = 0
                        for li in lista:
                            if kunden[0] in li:
                                values3[i,0:3] = values2[values2[:,0]==ll,0:3]
                                values3[i,3] = values2[values2[:,0]==ll,4]
                                values3[i,5] = counts2x[i]
                                ppp_and_group[runn,kunden,0] = values2[values2[:,0]==ll,2]
                                ppp_and_group[runn,kunden,1:4] = uniqSeg[ll]
                                ppp_and_group[runn,kunden,4:7] = uniqSegx[i]
                                j = uniqSegx[i,1]
                                k = values3[i,1].astype(int)
                                l = uniqSegx[i,0]
                                offerSets2 = np.copy(offerSets)
                                ofs = offerSets2[uniqSegx[i,2],:]
                                ofs2 = np.copy(ofs)
                                ofs2[k] = 0
                                values3[i,4] = max(0,1 - ((e**(segNP[0,l])+np.sum(ofs*e**(1/segScale[l]*(segQ[:,l]-np.squeeze(prices)))))/(e**(segNP[0,l])+e**(1/segScale[l]*(segQ[k,l]-values2[values2[:,0]==ll,3]))+np.sum(ofs2*e**(1/segScale[l]*(segQ[:,l]-np.squeeze(prices)))))))
                            ll += 1
                else:
                    time_elapsed_BI[runn] = timertime
                    for i in np.arange(uniqSeg.shape[0]):
                        vecmask = np.all(np.array([overviewx[runn,:,1:4]])==uniqSeg[i],2)
                        kunden = timeperiods[vecmask[0]]
                        ppp_and_group[runn,kunden,0] = values2[values2[:,0]==i,2] #neuen Preis reinschreiben in ppp_and_group
                        ppp_and_group[runn,kunden,1:4] = uniqSeg[i]
                        ppp_and_group[runn,kunden,4:7] = uniqSeg[i]
                        
                #generate new customer segments
                ppp_and_group2 = np.copy(ppp_and_group)
                ppp_and_group2[runn,:,3] = ppp_and_group2[runn,:,0]+np.squeeze(prices[(ppp_and_group[runn,:,2]).astype(int)]) #neuer Preis
                ppp_and_group2[runn,:,0:3] = ppp_and_group2[runn,:,4:7]
                ppp_and_group2 = ppp_and_group2[runn,:,0:4]
                uniqS, counts = np.unique(ppp_and_group2[:,:],return_counts=True,axis=0)
                uniqSeg = uniqS[1:]
                counts2 = counts[1:] 
                if  rem_cap[0][1:][-1] == 0:
                    counts2 = counts2[np.isin(uniqSeg[:,1],np.where(capcon[:,nRes-2]==1),invert=True)]
                    uniqSeg = uniqSeg[np.isin(uniqSeg[:,1],np.where(capcon[:,nRes-2]==1),invert=True)]   #delete those in highest compartment
                    counts2 = counts2[np.isin(uniqSeg[:,1],np.where(capcon[:,nRes-1]==2),invert=True)]
                    uniqSeg = uniqSeg[np.isin(uniqSeg[:,1],np.where(capcon[:,nRes-1]==2),invert=True)]   #delete those in highest compartment
                
                [uplist,klist,p_comp,uplist_M,y_M] = calc_upsellpossibilities(uphierar,uniqSeg,counts2,nProducts,nRes,offerSets,prices,capcon,rem_cap)
                    
                #save arrays to load into matlab     
                scipy.io.savemat('data_nopt.mat',{'inst':inst,'segNP':segNP,'nyp':nyp,'bound':bound,'eps':eps,'Y':y_M,'upMat':uplist_M,\
                                               'os':offerSets,'uniqSeg':uniqSeg,'prices':prices,'segQ':segQ,'capcon':capcon,'szpairs':szpairs,\
                                               'segScale':segScale,'cap':cap,'rem_cap':rem_cap,'cap':cap,'sharepairs':sharepairs})
# 
                revenues_optn[runn] =  eng.nopt(nargout=1)
                 
            else:
                cap_overview[runn,1:4,:] = rem_cap
                revenues_UP_run[runn] = -1000
                revenues_RCS_run[runn] = -1000
                revenues_RCS2_run[runn] = -1000
                revenues_UH_WSK_run[runn] = -1000
                revenues_UHbinom_run[runn] = -1000
                revenues_UH_run[runn] = -1000
                revenues_UH_newEps_run[runn] = -1000
        else:
            cap_overview[runn,1:4,:] = rem_cap
            revenues_UP_run[runn] = -1000
            revenues_RCS_run[runn] = -1000
            revenues_RCS2_run[runn] = -1000
            revenues_UH_WSK_run[runn] = -1000
            revenues_UHbinom_run[runn] = -1000
            revenues_UH_run[runn] = -1000
            revenues_UH_newEps_run[runn] = -1000
            
    up_overview_I = up_overview_I[1:]           #delete initialising row
    up_overview_II = up_overview_II[1:]
